package com.zeyuan.rpcclient.netty.ByteBuf;

import io.netty.buffer.*;

import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;

public class BytebufTest {
    public static void main(String[] args) {
        //bytebuf容量可以动态扩容,netty中bytebuffer不行，会报错
        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();
        System.out.println(buf);
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<300;i++){
            sb.append("a");
        }
        buf.writeBytes(sb.toString().getBytes(StandardCharsets.UTF_8));
        System.out.println(buf);
        //bytebuf支持直接内存和堆内存
        //基于堆
        ByteBuf buffer = ByteBufAllocator.DEFAULT.heapBuffer(10);
        ByteBuf directBuf = ByteBufAllocator.DEFAULT.directBuffer(10);//直接内存分配慢(创建销毁代价高，读写性能高少一次内存复制，适合配合池化功能，对gc压力小，不受jvm管理)但读写快
        //池化可以重用bytebuf，开启池化: -Dio.netty.allocator.type={unpolled|polled}默认开启
        //bytebuf将读写分成两个指针，不用像bytebuffer一样flip，clear，compact切换读写模式，而且可以动态扩容(合理设置最大容量)
        /*
        * writeBoolean(boolean) 用01|00代表true|false
        * writeInt(int) 大端写入 big endian  0x250写入后变成00 00 02 50 先写高位字节，一般都用大端
        * writeIntLE(int) 小端写入 little endian 0x250写入后变成50 02 00 00 先写低位字节
        * writeCharsequesnce(charsequesce)可以写入string以及相关类
        *
        * */
        /*
        * 扩容：写入后数据大小未超过512，则选择下一个16的整数倍，写入后大小12，扩容后capacity=16
        *       超过512，选择下一个2^n,写入后大小513，capacity扩容为2^10=1024(2^9=512)
        * 扩容不能超过max  capacity会报错
        * */
        /*读取: buffer.readByte()按字节读取
                重复读取：buffer.markReaderIndex();记录读指针，buffer.readInt就能读取这个下标下的东西了
         */
        /*
        * bytebuf的内存回收：
        *   unpooledheapbytebuf用jvm内存，gc回收
        * unpooleddirectbytebuf直接内存，特殊方法回收
        * pooledbytebuf和子类 池化机制，复杂规则回收
        *
        * netty采用引用计数法控制回收内存，每个bytebuf实现了referencecounted接口
        *   每个bytebuf对象初试计数为1
        *   调用release()计数减1，如果计数为0，bytebuf回收
        * 调用retain()计数+1，
        * 计数为0底层内存会回收，bytebuf无法使用。 哪个handler最后使用bytebuf则负责释放这个bytebuf
        * */

        /*
        * 零拷贝bytebuf的slice()方法: 切分bytebuf
        *   对原始bytebuf切片成多个bytebuf，并没有内存复制，使用的还是原来的bytebuf的内存，切片后的bytebuf维护独立的read write
        *   如果添加元素会影响切片，所以禁止修改切片（writebytes写入新字符串会报错）
        * */
        /*
        * duplicate()复制bytebuf，没内存复制，还是跟原来同一快内存
        * copy() 深拷贝，有内存复制
        * */

        /*
        * bytebuf多个组合成一个，且没有内存复制
        *
        * */
        ByteBuf bu = ByteBufAllocator.DEFAULT.buffer();
        bu.writeBytes("".getBytes(StandardCharsets.UTF_8)).writeBytes("".getBytes());//多次内存复制，结果对但性能差
        CompositeByteBuf byteBufs = ByteBufAllocator.DEFAULT.compositeBuffer();

        ByteBuf buffer1 = ByteBufAllocator.DEFAULT.buffer();
        ByteBuf buffer2 = ByteBufAllocator.DEFAULT.buffer();
        buffer1.writeBytes("".getBytes(StandardCharsets.UTF_8));
        buffer2.writeBytes("".getBytes(StandardCharsets.UTF_8));
        byteBufs.addComponents(true,buffer1,buffer2); //true表示自动调整插入指针，不写无法成功,这样就在不发生内存复制的情况下组合了两个bytebuf

        /*unpolled工具类提供非池化bytebuf创建组合复制等操作*/
        ByteBuf buffer3 = Unpooled.wrappedBuffer(buffer1, buffer2); //包装bytebuf成一个新的，底层用compositebytebuf避免内存复制
        ByteBuf buf4 = Unpooled.wrappedBuffer(new byte[]{1, 2, 3}, new byte[]{4, 5, 6});

        System.out.println(ByteBufUtil.prettyHexDump(buffer3));
        /*
        * bytebuf优势：
        *   池化思想重用
        *   读写指针分离，不用bytebuffer一样切换读写模式
        *   自动扩容
        *   支持链式调用
        *   零拷贝体现多，slice duplicate compositebytebuf
        * */

        /*
        * bytebuf创建一般在handler中，用channelread中的ctx.alloc().buffer(20)而不是ByteBufAllocator.DEFAULT.buffer();
        * 如果需要双向通信，server返回给client，只需要在handler中ctx.writeandflush即可返回给客户端
        *
        * java socket全双工，读写可以同时执行，读写不会互相阻塞，即使是阻塞io
        * */
        List<Object> objects = new LinkedList<>();
//        objects.
    }
}
